<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <meta name="keywords" content="Hexo Theme Keep">
    <meta name="description" content="Hexo Theme Keep">
    <meta name="author" content="Tckapco">
    
    <title>
        
            运行原理——EventLoop |
        
        Tckapco
    </title>
    
<link rel="stylesheet" href="/css/style.css">

    <link rel="shortcut icon" href="/images/logo.svg">
    <link rel="stylesheet" href="//cdn.jsdelivr.net/npm/hexo-theme-keep@3.4.5/source/css/font-awesome.min.css">
    <script id="hexo-configurations">
    let KEEP = window.KEEP || {};
    KEEP.hexo_config = {"hostname":"theboat.gitee.io","root":"/","language":"en"};
    KEEP.theme_config = {"toc":{"enable":true,"number":true,"expand_all":true,"init_open":true},"style":{"primary_color":"#0066CC","avatar":"/images/avatar.jpg","favicon":"/images/logo.svg","article_img_align":"left","left_side_width":"260px","content_max_width":"920px","hover":{"shadow":false,"scale":true},"first_screen":{"enable":true,"background_img":"/images/bg.svg","description":"Keep writing and Keep loving."},"scroll":{"progress_bar":{"enable":false},"percent":{"enable":false}}},"local_search":{"enable":false,"preload":false},"code_copy":{"enable":true,"style":"mac"},"pjax":{"enable":false},"lazyload":{"enable":false},"version":"3.4.5"};
    KEEP.language_ago = {"second":"%s seconds ago","minute":"%s minutes ago","hour":"%s hours ago","day":"%s days ago","week":"%s weeks ago","month":"%s months ago","year":"%s years ago"};
  </script>
<meta name="generator" content="Hexo 5.4.0"></head>


<body>
<div class="progress-bar-container">
    

    
</div>


<main class="page-container">

    

    <div class="page-main-content">

        <div class="page-main-content-top">
            <header class="header-wrapper">

    <div class="header-content">
        <div class="left">
            
            <a class="logo-title" href="/">
                Tckapco
            </a>
        </div>

        <div class="right">
            <div class="pc">
                <ul class="menu-list">
                    
                        <li class="menu-item">
                            <a class=""
                               href="/"
                            >
                                HOME
                            </a>
                        </li>
                    
                        <li class="menu-item">
                            <a class=""
                               href="/archives"
                            >
                                ARCHIVES
                            </a>
                        </li>
                    
                        <li class="menu-item">
                            <a class=""
                               href="/categories"
                            >
                                CATEGORIES
                            </a>
                        </li>
                    
                        <li class="menu-item">
                            <a class=""
                               href="/links"
                            >
                                LINKS
                            </a>
                        </li>
                    
                    
                </ul>
            </div>
            <div class="mobile">
                
                <div class="icon-item menu-bar">
                    <div class="menu-bar-middle"></div>
                </div>
            </div>
        </div>
    </div>

    <div class="header-drawer">
        <ul class="drawer-menu-list">
            
                <li class="drawer-menu-item flex-center">
                    <a class=""
                       href="/">HOME</a>
                </li>
            
                <li class="drawer-menu-item flex-center">
                    <a class=""
                       href="/archives">ARCHIVES</a>
                </li>
            
                <li class="drawer-menu-item flex-center">
                    <a class=""
                       href="/categories">CATEGORIES</a>
                </li>
            
                <li class="drawer-menu-item flex-center">
                    <a class=""
                       href="/links">LINKS</a>
                </li>
            
        </ul>
    </div>

    <div class="window-mask"></div>

</header>


        </div>

        <div class="page-main-content-middle">

            <div class="main-content">

                
                    <div class="fade-in-down-animation">
    <div class="article-content-container">

        <div class="article-title">
            <span class="title-hover-animation">运行原理——EventLoop</span>
        </div>

        
            <div class="article-header">
                <div class="avatar">
                    <img src="/images/avatar.jpg">
                </div>
                <div class="info">
                    <div class="author">
                        <span class="name">Tckapco</span>
                        
                            <span class="author-label">Lv2</span>
                        
                    </div>
                    <div class="meta-info">
                        <div class="article-meta-info">
    <span class="article-date article-meta-item">
        <i class="fas fa-edit"></i>&nbsp;
        <span class="pc">2021-06-05 12:13:01</span>
        <span class="mobile">2021-06-05 12:13</span>
    </span>
    
        <span class="article-categories article-meta-item">
            <i class="fas fa-folder"></i>&nbsp;
            <ul>
                
                    <li>
                        <a href="/categories/JavaScript/">JavaScript</a>&nbsp;
                    </li>
                
            </ul>
        </span>
    
    
        <span class="article-tags article-meta-item">
            <i class="fas fa-tags"></i>&nbsp;
            <ul>
                
                    <li>
                        <a href="/tags/%E8%BF%90%E8%A1%8C%E5%8E%9F%E7%90%86/">运行原理</a>&nbsp;
                    </li>
                
            </ul>
        </span>
    

    
    
        <span class="article-wordcount article-meta-item">
            <i class="fas fa-file-word"></i>&nbsp;<span>5k Words</span>
        </span>
    
    
        <span class="article-min2read article-meta-item">
            <i class="fas fa-clock"></i>&nbsp;<span>19 Mins</span>
        </span>
    
    
</div>

                    </div>
                </div>
            </div>
        

        <div class="article-content markdown-body">
            <p><img src="https://gitee.com/theBoat/picgo/raw/master/HLfu9g.jpg" alt="img"></p>
<h1 id="1-进程与线程"><a href="#1-进程与线程" class="headerlink" title="1.进程与线程"></a>1.进程与线程</h1><h2 id="概念"><a href="#概念" class="headerlink" title="概念"></a>概念</h2><blockquote>
<p>相信大家经常会听到 JS 是单线程执行的，但是你是否疑惑过什么是线程？</p>
</blockquote>
<blockquote>
<p>百度百科：</p>
<p><strong>进程</strong></p>
<p>狭义定义：进程是正在运行的程序的实例（an instance of a computer program that is being executed）。</p>
<p>广义定义：进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。</p>
<p><strong>线程</strong></p>
<p>是进程中的实际运作单位</p>
</blockquote>
<p>讲到线程，那么肯定也得说一下进程。本质上来说，两个名词都是 CPU 工作时间片的一个描述。<br>进程描述了 CPU 在运行指令及加载和保存上下文所需的时间，放在应用上来说就代表了一个程序。线程是进程中 的更小单位，描述了执行一段指令所需的时间。<br>把这些概念拿到浏览器中来说，当你打开一个 Tab 页时，其实就是创建了一个进程，<strong>一个进程中可以有多个线 程</strong>，比如渲染线程、JS 引擎线程、HTTP 请求线程等等。当你发起一个请求时，其实就是创建了一个线程，当请 求结束后，该线程可能就会被销毁。<br>上文说到了 JS 引擎线程和渲染线程，大家应该都知道，在 JS 运行的时候可能会阻止 UI 渲染，这说明了两个线程 是互斥的。这其中的原因是因为 JS 可以修改 DOM，如果在 JS 执行的时候 UI 线程还在工作，就可能导致不能安 全的渲染 UI。这其实也是一个单线程的好处，得益于 JS 是单线程运行的，可以达到<strong>节省内存，节约上下文切换时间，没有锁的问题的好处</strong>。当然前面两点在服务端中更容易体现，对于锁的问题，形象的来说就是当我读取一个数 字 15 的时候，同时有两个操作对数字进行了加减，这时候结果就出现了错误。解决这个问题也不难，只需要在读 取的时候加锁，直到读取完毕之前都不能进行写入操作</p>
<h2 id="多线程"><a href="#多线程" class="headerlink" title="多线程"></a>多线程</h2><p>在最新的HTML5中提出了Web-Worker，但javascript是单线程这一核心仍未改变。所以一切javascript版的”多线程”都是用单线程模拟出来的，一切javascript多线程都是纸老虎！</p>
<h1 id="2-执行栈-Call-stack"><a href="#2-执行栈-Call-stack" class="headerlink" title="2.执行栈(Call stack)"></a>2.执行栈(Call stack)</h1><blockquote>
<p>可以把执行栈认为是一个存储函数调用的栈结构，遵循先进后出的原则。</p>
</blockquote>
<p>执行栈也称为调用栈，调用栈是解释器（比如浏览器中的 JavaScript 解释器）追踪函数执行流的一种机制。当执行环境中调用了多个<a class="link"   target="_blank" rel="noopener" href="https://developer.mozilla.org/zh-CN/docs/Glossary/Function" >函数<i class="fas fa-external-link-alt"></i></a>时，通过这种机制，我们能够追踪到哪个函数正在执行，执行的函数体中又调用了哪个函数</p>
<p>举个栗子：</p>
<p><img src="https://pic3.zhimg.com/v2-7aaf98aa1b0cc64943e178c28d1d676e_b.webp" alt="img"></p>
<p><strong>执行顺序：</strong></p>
<p>首先会执行main函数，然后</p>
<ol>
<li>忽略前面所有函数，直到bar()被调用</li>
<li>把bar()添加进调用栈列表</li>
<li>执行bar()</li>
<li>执行到foo()时，该函数被调用</li>
<li>把foo()添加进调用栈列表</li>
<li>执行foo()函数体中的代码，直到全部执行完毕</li>
</ol>
<p><strong>总结</strong></p>
<ul>
<li>每调用一个函数，解释器就会把该函数添加进调用栈并开始执行。</li>
<li>正在调用栈中执行的函数还调用了其它函数，那么新函数也将会被添加进调用栈，一旦这个函数被调用，便会立即执行。</li>
<li>当前函数执行完毕后，解释器将其<strong>清出调用栈</strong>，继续执行当前执行环境下的剩余的代码。</li>
<li>当分配的调用栈空间被占满时，会引发“堆栈溢出”错误。</li>
</ul>
<h1 id="3-宏任务与微任务"><a href="#3-宏任务与微任务" class="headerlink" title="3.宏任务与微任务"></a>3.宏任务与微任务</h1><h2 id="区别"><a href="#区别" class="headerlink" title="区别"></a>区别</h2><p><strong>栗子</strong></p>
<p>这个就像去银行办业务一样，先要取号进行排号。<br> 一般上边都会印着类似：“您的号码为XX，前边还有XX人。”之类的字样。</p>
<p>因为柜员同时职能处理一个来办理业务的客户，这时每一个来办理业务的人就可以认为是银行柜员的一个宏任务来存在的，当柜员处理完当前客户的问题以后，选择接待下一位，广播报号，也就是下一个宏任务的开始。<br> 所以多个宏任务合在一起就可以认为说有一个任务队列在这，里边是当前银行中所有排号的客户。<br><strong>任务队列中的都是已经完成的异步操作，而不是说注册一个异步任务就会被放在这个任务队列中，就像在银行中排号，如果叫到你的时候你不在，那么你当前的号牌就作废了，柜员会选择直接跳过进行下一个客户的业务处理，等你回来以后还需要重新取号</strong></p>
<p>而且一个宏任务在执行的过程中，是可以添加一些微任务的，就像在柜台办理业务，你前边的一位老大爷可能在存款，在存款这个业务办理完以后，柜员会问老大爷还有没有其他需要办理的业务，这时老大爷想了一下：“最近P2P爆雷有点儿多，是不是要选择稳一些的理财呢”，然后告诉柜员说，要办一些理财的业务，这时候柜员肯定不能告诉老大爷说：“您再上后边取个号去，重新排队”。<br> 所以本来快轮到你来办理业务，会因为老大爷临时添加的“<strong>理财业务</strong>”而往后推。<br> 也许老大爷在办完理财以后还想 <strong>再办一个信用卡</strong>？或者 <strong>再买点儿纪念币</strong>？<br> 无论是什么需求，只要是柜员能够帮她办理的，都会在处理你的业务之前来做这些事情，这些都可以认为是微任务。</p>
<p>这就说明：<strong>在当前的微任务没有执行完成时，是不会执行下一个宏任务的。</strong></p>
<p>所以就有了那个经常在面试题、各种博客中的代码片段：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">setTimeout(_ &#x3D;&gt; console.log(4))</span><br><span class="line"></span><br><span class="line">new Promise(resolve &#x3D;&gt; &#123;</span><br><span class="line">  resolve()</span><br><span class="line">  console.log(1)</span><br><span class="line">&#125;).then(_ &#x3D;&gt; &#123;</span><br><span class="line">  console.log(3)</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">console.log(2)</span><br></pre></td></tr></table></figure>

<p><code>setTimeout</code>就是作为宏任务来存在的，而<code>Promise.then</code>则是具有代表性的微任务，上述代码的执行顺序就是按照序号来输出的。</p>
<p><strong>所有会进入的异步都是指的事件回调中的那部分代码</strong><br> 也就是说<code>new Promise</code>在实例化的过程中所执行的代码都是同步进行的，而<code>then</code>中注册的回调才是异步执行的。<br> 在同步代码执行完成后才回去检查是否有异步任务完成，并执行对应的回调，而微任务又会在宏任务之前执行。<br> 所以就得到了上述的输出结论<code>1、2、3、4</code>。</p>
<p><em>+部分表示同步执行的代码</em></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">+setTimeout(_ &#x3D;&gt; &#123;</span><br><span class="line">-  console.log(4)</span><br><span class="line">+&#125;)</span><br><span class="line"></span><br><span class="line">+new Promise(resolve &#x3D;&gt; &#123;</span><br><span class="line">+  resolve()</span><br><span class="line">+  console.log(1)</span><br><span class="line">+&#125;).then(_ &#x3D;&gt; &#123;</span><br><span class="line">-  console.log(3)</span><br><span class="line">+&#125;)</span><br><span class="line"></span><br><span class="line">+console.log(2)</span><br></pre></td></tr></table></figure>

<p>本来<code>setTimeout</code>已经先设置了定时器（相当于取号），然后在当前进程中又添加了一些<code>Promise</code>的处理（临时添加业务）。</p>
<p>所以进阶的，即便我们继续在<code>Promise</code>中实例化<code>Promise</code>，其输出依然会早于<code>setTimeout</code>的宏任务：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">setTimeout(_ &#x3D;&gt; console.log(4))</span><br><span class="line"></span><br><span class="line">new Promise(resolve &#x3D;&gt; &#123;</span><br><span class="line">  resolve()</span><br><span class="line">  console.log(1)</span><br><span class="line">&#125;).then(_ &#x3D;&gt; &#123;</span><br><span class="line">  console.log(3)</span><br><span class="line">  Promise.resolve().then(_ &#x3D;&gt; &#123;</span><br><span class="line">    console.log(&#39;before timeout&#39;)</span><br><span class="line">  &#125;).then(_ &#x3D;&gt; &#123;</span><br><span class="line">    Promise.resolve().then(_ &#x3D;&gt; &#123;</span><br><span class="line">      console.log(&#39;also before timeout&#39;)</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">console.log(2)</span><br></pre></td></tr></table></figure>

<p>当然了，实际情况下很少会有简单的这么调用<code>Promise</code>的，一般都会在里边有其他的异步操作，比如<code>fetch</code>、<code>fs.readFile</code>之类的操作。<br> 而这些其实就相当于注册了一个宏任务，而非是微任务。</p>
<p><em>P.S. 在<a class="link"   target="_blank" rel="noopener" href="https://promisesaplus.com/#notes" >Promise/A+的规范<i class="fas fa-external-link-alt"></i></a>中，<code>Promise</code>的实现可以是微任务，也可以是宏任务，但是普遍的共识表示(至少<code>Chrome</code>是这么做的)，<code>Promise</code>应该是属于微任务阵营的</em></p>
<p><strong>其他区别</strong></p>
<p><strong>宏任务是由宿主（浏览器、Node）发起的，而微任务由 JS 自身发起</strong></p>
<p>所以，明白哪些操作是宏任务、哪些是微任务就变得很关键，这是目前业界比较流行的说法：</p>
<h2 id="宏任务"><a href="#宏任务" class="headerlink" title="宏任务"></a>宏任务</h2><table>
<thead>
<tr>
<th>#</th>
<th>浏览器</th>
<th>Node</th>
</tr>
</thead>
<tbody><tr>
<td><code>I/O</code></td>
<td>✅</td>
<td>✅</td>
</tr>
<tr>
<td><code>setTimeout</code></td>
<td>✅</td>
<td>✅</td>
</tr>
<tr>
<td><code>setInterval</code></td>
<td>✅</td>
<td>✅</td>
</tr>
<tr>
<td><code>setImmediate</code></td>
<td>❌</td>
<td>✅</td>
</tr>
<tr>
<td><code>requestAnimationFrame</code></td>
<td>✅</td>
<td>❌</td>
</tr>
<tr>
<td>script（整体代码块）</td>
<td>✅</td>
<td></td>
</tr>
</tbody></table>
<p><em>有些地方会列出来<code>UI Rendering</code>，说这个也是宏任务，可是在读了<a class="link"   target="_blank" rel="noopener" href="https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model" >HTML规范文档<i class="fas fa-external-link-alt"></i></a>以后，发现这很显然是和微任务平行的一个操作步骤</em><br> <em><code>requestAnimationFrame</code>姑且也算是宏任务吧，<code>requestAnimationFrame</code>在<a class="link"   target="_blank" rel="noopener" href="https://developer.mozilla.org/zh-CN/docs/Web/API/Window/requestAnimationFrame" >MDN的定义<i class="fas fa-external-link-alt"></i></a>为，下次页面重绘前所执行的操作，而重绘也是作为宏任务的一个步骤来存在的，且该步骤晚于微任务的执行</em></p>
<p><strong>如何理解script（整体代码块）是个宏任务呢？</strong></p>
<p>实际上如果同时存在两个 script 代码块，会首先在执行第一个 script 代码块中的同步代码，如果这个过程中创建了微任务并进入了微任务队列，第一个 script 同步代码执行完之后，会首先去清空微任务队列，再去开启第二个 script 代码块的执行。所以这里应该就可以理解 script（整体代码块）为什么会是宏任务。</p>
<h2 id="微任务"><a href="#微任务" class="headerlink" title="微任务"></a>微任务</h2><table>
<thead>
<tr>
<th>#</th>
<th>浏览器</th>
<th>Node</th>
</tr>
</thead>
<tbody><tr>
<td><code>process.nextTick</code></td>
<td>❌</td>
<td>✅</td>
</tr>
<tr>
<td><code>MutationObserver</code></td>
<td>✅</td>
<td>❌</td>
</tr>
<tr>
<td><code>Promise.then catch finally</code></td>
<td>✅</td>
<td>✅</td>
</tr>
</tbody></table>
<h1 id="4-EventLoop"><a href="#4-EventLoop" class="headerlink" title="4.EventLoop"></a>4.EventLoop</h1><blockquote>
<p>EventLoop是一种防止js造成阻塞的程序执行机制</p>
</blockquote>
<p>上边一直在讨论 宏任务、微任务，各种任务的执行。<br>但是回到现实，<code>JavaScript</code>是一个单进程的语言，同一时间不能处理多个任务，所以何时执行宏任务，何时执行微任务？我们需要有这样的一个判断逻辑存在。</p>
<p>每办理完一个业务，柜员就会问当前的客户，是否还有其他需要办理的业务。<em><strong>（检查还有没有微任务需要处理）</strong></em><br>而客户明确告知说没有事情以后，柜员就去查看后边还有没有等着办理业务的人。<em><strong>（结束本次宏任务、检查还有没有宏任务需要处理）</strong></em><br>这个检查的过程是持续进行的，每完成一个任务都会进行一次，而这样的操作就被称为<code>Event Loop</code>。**(这是个非常简易的描述了，实际上会复杂很多)**</p>
<h2 id="浏览器"><a href="#浏览器" class="headerlink" title="浏览器"></a>浏览器</h2><p>举个栗子：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">console.log(&#39;script start&#39;);</span><br><span class="line"></span><br><span class="line">setTimeout(function() &#123;</span><br><span class="line">  console.log(&#39;setTimeout&#39;);</span><br><span class="line">&#125;, 0);</span><br><span class="line"></span><br><span class="line">Promise.resolve().then(function() &#123;</span><br><span class="line">  console.log(&#39;promise1&#39;);</span><br><span class="line">&#125;).then(function() &#123;</span><br><span class="line">  console.log(&#39;promise2&#39;);</span><br><span class="line">&#125;);</span><br><span class="line">console.log(&#39;script end&#39;);</span><br></pre></td></tr></table></figure>

<p>首先我们划分几个分类：</p>
<p><strong>第一次执行：</strong></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">Tasks：run script、 setTimeout callback</span><br><span class="line"></span><br><span class="line">Microtasks：Promise then	</span><br><span class="line"></span><br><span class="line">JS stack: script	</span><br><span class="line">Log: script start、script end。</span><br></pre></td></tr></table></figure>

<p>执行同步代码，将宏任务（<code>Tasks</code>）和微任务(<code>Microtasks</code>)划分到各自队列中。</p>
<p><strong>第二次执行：</strong></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">Tasks：run script、 setTimeout callback</span><br><span class="line"></span><br><span class="line">Microtasks：Promise2 then	</span><br><span class="line"></span><br><span class="line">JS stack: Promise2 callback	</span><br><span class="line">Log: script start、script end、promise1、promise2</span><br></pre></td></tr></table></figure>

<p>执行宏任务后，检测到微任务(<code>Microtasks</code>)队列中不为空，执行<code>Promise1</code>，执行完成<code>Promise1</code>后，调用<code>Promise2.then</code>，放入微任务(<code>Microtasks</code>)队列中，再执行<code>Promise2.then</code>。</p>
<p><strong>第三次执行：</strong></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">Tasks：setTimeout callback</span><br><span class="line"></span><br><span class="line">Microtasks：	</span><br><span class="line"></span><br><span class="line">JS stack: setTimeout callback</span><br><span class="line">Log: script start、script end、promise1、promise2、setTimeout</span><br></pre></td></tr></table></figure>

<p>当微任务(<code>Microtasks</code>)队列中为空时，执行宏任务（<code>Tasks</code>），执行<code>setTimeout callback</code>，打印日志。</p>
<p><strong>第四次执行：</strong></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">Tasks：setTimeout callback</span><br><span class="line"></span><br><span class="line">Microtasks：	</span><br><span class="line"></span><br><span class="line">JS stack: </span><br><span class="line">Log: script start、script end、promise1、promise2、setTimeout</span><br></pre></td></tr></table></figure>

<p>清空<strong>Tasks</strong>队列和<code>JS stack</code>。</p>
<p>以上执行帧动画可以查看<a class="link"   target="_blank" rel="noopener" href="https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/" >Tasks, microtasks, queues and schedules<i class="fas fa-external-link-alt"></i></a><br> 或许这张图也更好理解些。</p>
<p><img src="https://user-gold-cdn.xitu.io/2019/1/18/16860ae5ad02f993?imageslim" alt="img"></p>
<blockquote>
<p>执行步骤：<br>1.执行main函数，执行<strong>同步代码</strong>(注意异步代码是指事件回调中的那部分代码)</p>
<p>2.执行微任务(整个script脚本作为宏任务)</p>
<p>3.调用第一个宏任务，执行完后调用其微任务</p>
<p>4.再调用下一个宏任务，直至所有代码执行完毕</p>
</blockquote>
<p>详细解析版：</p>
<ol>
<li>执行全局Script同步代码，这些同步代码有一些是同步语句，有一些是异步语句（比如setTimeout等）；</li>
<li>全局Script代码执行完毕后，调用栈Stack会清空；</li>
<li>从微队列microtask queue中取出位于队首的回调任务，放入调用栈Stack中执行，执行完后microtask queue长度减1；</li>
<li>继续取出位于队首的任务，放入调用栈Stack中执行，以此类推，直到直到把microtask queue中的所有任务都执行完毕。<strong>注意，如果在执行microtask的过程中，又产生了microtask，那么会加入到队列的末尾，也会在这个周期被调用执行</strong>；</li>
<li>microtask queue中的所有任务都执行完毕，此时microtask queue为空队列，调用栈Stack也为空；</li>
<li>取出宏队列macrotask queue中位于队首的任务，放入Stack中执行；</li>
<li>执行完毕后，调用栈Stack为空；</li>
<li>重复第3-7个步骤；</li>
<li>重复第3-7个步骤；</li>
<li>……</li>
</ol>
<p><strong>自我检验</strong></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line">console.log(&#39;1&#39;);</span><br><span class="line"></span><br><span class="line">setTimeout(function() &#123;</span><br><span class="line">    console.log(&#39;2&#39;);</span><br><span class="line">    process.nextTick(function() &#123;</span><br><span class="line">        console.log(&#39;3&#39;);</span><br><span class="line">    &#125;)</span><br><span class="line">    new Promise(function(resolve) &#123;</span><br><span class="line">        console.log(&#39;4&#39;);</span><br><span class="line">        resolve();</span><br><span class="line">    &#125;).then(function() &#123;</span><br><span class="line">        console.log(&#39;5&#39;)</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;)</span><br><span class="line">process.nextTick(function() &#123;</span><br><span class="line">    console.log(&#39;6&#39;);</span><br><span class="line">&#125;)</span><br><span class="line">new Promise(function(resolve) &#123;</span><br><span class="line">    console.log(&#39;7&#39;);</span><br><span class="line">    resolve();</span><br><span class="line">&#125;).then(function() &#123;</span><br><span class="line">    console.log(&#39;8&#39;)</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">setTimeout(function() &#123;</span><br><span class="line">    console.log(&#39;9&#39;);</span><br><span class="line">    process.nextTick(function() &#123;</span><br><span class="line">        console.log(&#39;10&#39;);</span><br><span class="line">    &#125;)</span><br><span class="line">    new Promise(function(resolve) &#123;</span><br><span class="line">        console.log(&#39;11&#39;);</span><br><span class="line">        resolve();</span><br><span class="line">    &#125;).then(function() &#123;</span><br><span class="line">        console.log(&#39;12&#39;)</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<p>输出结果：</p>
<p>1，7，6，8，2，4，3，5，9，11，10，12</p>
<p>该结果应在node环境下执行</p>
<h2 id="Node"><a href="#Node" class="headerlink" title="Node"></a>Node</h2><p><img src="https://gitee.com/theBoat/picgo/raw/master/image-20220220160055189.png" alt="image-20220220160055189"></p>
<p><code>Node</code>中的<code>Event Loop</code>是基于<code>libuv</code>实现的，而<code>libuv</code>是 <code>Node</code> 的新跨平台抽象层，libuv使用异步，事件驱动的编程方式，核心是提供<code>i/o</code>的事件循环和异步回调。libuv的<code>API</code>包含有时间，非阻塞的网络，异步文件操作，子进程等等。 <code>Event Loop</code>就是在<code>libuv</code>中实现的。</p>
<p><img src="https://gitee.com/theBoat/picgo/raw/master/image-20220220155938728.png" alt="image-20220220155938728"></p>
<p><strong><code>Node</code>的<code>Event loop</code>一共分为6个阶段，每个细节具体如下：</strong></p>
<ul>
<li><code>timers</code>: 执行<code>setTimeout</code>和<code>setInterval</code>中到期的<code>callback</code>。</li>
<li><code>pending callback</code>: 上一轮循环中少数的<code>callback</code>会放在这一阶段执行。</li>
<li><code>idle, prepare</code>: 仅在内部使用。</li>
<li><code>poll</code>: 最重要的阶段，执行<code>pending callback</code>，在适当的情况下回阻塞在这个阶段。</li>
<li><code>check</code>: 执行<code>setImmediate</code>(<code>setImmediate()</code>是将事件插入到事件队列尾部，主线程和事件队列的函数执行完成之后立即执行<code>setImmediate</code>指定的回调函数)的<code>callback</code>。</li>
<li><code>close callbacks</code>: 执行<code>close</code>事件的<code>callback</code>，例如<code>socket.on(&#39;close&#39;[,fn])</code>或者<code>http.server.on(&#39;close, fn)</code>。</li>
</ul>
<p>具体细节如下：</p>
<p><strong>timers</strong></p>
<p>执行<code>setTimeout</code>和<code>setInterval</code>中到期的<code>callback</code>，执行这两者回调需要设置一个毫秒数，理论上来说，应该是时间一到就立即执行callback回调，但是由于<code>system</code>的调度可能会延时，达不到预期时间。<br> 以下是官网文档解释的例子：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">const fs &#x3D; require(&#39;fs&#39;);</span><br><span class="line"></span><br><span class="line">function someAsyncOperation(callback) &#123;</span><br><span class="line">  &#x2F;&#x2F; Assume this takes 95ms to complete</span><br><span class="line">  fs.readFile(&#39;&#x2F;path&#x2F;to&#x2F;file&#39;, callback);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">const timeoutScheduled &#x3D; Date.now();</span><br><span class="line"></span><br><span class="line">setTimeout(() &#x3D;&gt; &#123;</span><br><span class="line">  const delay &#x3D; Date.now() - timeoutScheduled;</span><br><span class="line"></span><br><span class="line">  console.log(&#96;$&#123;delay&#125;ms have passed since I was scheduled&#96;);</span><br><span class="line">&#125;, 100);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F; do someAsyncOperation which takes 95 ms to complete</span><br><span class="line">someAsyncOperation(() &#x3D;&gt; &#123;</span><br><span class="line">  const startCallback &#x3D; Date.now();</span><br><span class="line"></span><br><span class="line">  &#x2F;&#x2F; do something that will take 10ms...</span><br><span class="line">  while (Date.now() - startCallback &lt; 10) &#123;</span><br><span class="line">    &#x2F;&#x2F; do nothing</span><br><span class="line">  &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<p>当进入事件循环时，它有一个空队列（<code>fs.readFile()</code>尚未完成），因此定时器将等待剩余毫秒数，当到达95ms时，<code>fs.readFile()</code>完成读取文件并且其完成需要10毫秒的回调被添加到轮询队列并执行。<br> 当回调结束时，队列中不再有回调，因此事件循环将看到已达到最快定时器的<strong>阈值</strong>，然后回到<strong>timers阶段</strong>以执行定时器的回调。</p>
<p>在此示例中，您将看到正在调度的计时器与正在执行的回调之间的总延迟将为105毫秒。</p>
<p><strong>以下是我测试时间：</strong></p>
<p><img src="https://gitee.com/theBoat/picgo/raw/master/image-20220220155806573.png" alt="image-20220220155806573"></p>
<p><strong>pending callbacks</strong></p>
<p>此阶段执行某些系统操作（例如TCP错误类型）的回调。  例如，如果<code>TCP socket ECONNREFUSED</code>在尝试connect时receives，则某些* nix系统希望等待报告错误。 这将在<code>pending callbacks</code>阶段执行。</p>
<p><strong>poll</strong></p>
<p><strong>该poll阶段有两个主要功能：</strong></p>
<ul>
<li>执行<code>I/O</code>回调。</li>
<li>处理轮询队列中的事件。</li>
</ul>
<p><strong>当事件循环进入<code>poll</code>阶段并且在<code>timers</code>中没有可以执行定时器时，将发生以下两种情况之一</strong></p>
<ul>
<li>如果<code>poll</code>队列不为空，则事件循环将遍历其同步执行它们的<code>callback</code>队列，直到队列为空，或者达到<code>system-dependent</code>（系统相关限制）。</li>
</ul>
<p><strong>如果<code>poll</code>队列为空，则会发生以下两种情况之一</strong></p>
<ul>
<li>如果有<code>setImmediate()</code>回调需要执行，则会立即停止执行<code>poll</code>阶段并进入执行<code>check</code>阶段以执行回调。</li>
<li>如果没有<code>setImmediate()</code>回到需要执行，poll阶段将等待<code>callback</code>被添加到队列中，然后立即执行。</li>
</ul>
<p><strong>当然设定了 timer 的话且 poll 队列为空，则会判断是否有 timer 超时，如果有的话会回到 timer 阶段执行回调。</strong></p>
<p><strong>check</strong></p>
<p><strong>此阶段允许人员在poll阶段完成后立即执行回调。</strong><br> 如果<code>poll</code>阶段闲置并且<code>script</code>已排队<code>setImmediate()</code>，则事件循环到达check阶段执行而不是继续等待。</p>
<p><code>setImmediate()</code>实际上是一个特殊的计时器，它在事件循环的一个单独阶段运行。它使用<code>libuv API</code>来调度在<code>poll</code>阶段完成后执行的回调。</p>
<p>通常，当代码被执行时，事件循环最终将达到<code>poll</code>阶段，它将等待传入连接，请求等。<br> 但是，如果已经调度了回调<code>setImmediate()</code>，并且轮询阶段变为空闲，则它将结束并且到达<code>check</code>阶段，而不是等待<code>poll</code>事件。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">console.log(&#39;start&#39;)</span><br><span class="line">setTimeout(() &#x3D;&gt; &#123;</span><br><span class="line">  console.log(&#39;timer1&#39;)</span><br><span class="line">  Promise.resolve().then(function() &#123;</span><br><span class="line">    console.log(&#39;promise1&#39;)</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;, 0)</span><br><span class="line">setTimeout(() &#x3D;&gt; &#123;</span><br><span class="line">  console.log(&#39;timer2&#39;)</span><br><span class="line">  Promise.resolve().then(function() &#123;</span><br><span class="line">    console.log(&#39;promise2&#39;)</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;, 0)</span><br><span class="line">Promise.resolve().then(function() &#123;</span><br><span class="line">  console.log(&#39;promise3&#39;)</span><br><span class="line">&#125;)</span><br><span class="line">console.log(&#39;end&#39;)</span><br></pre></td></tr></table></figure>

<p>如果<code>node</code>版本为<code>v11.x</code>， 其结果与浏览器一致。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">start</span><br><span class="line">end</span><br><span class="line">promise3</span><br><span class="line">timer1</span><br><span class="line">promise1</span><br><span class="line">timer2</span><br><span class="line">promise2</span><br></pre></td></tr></table></figure>

<p>具体详情可以查看《<a class="link"   target="_blank" rel="noopener" href="https://juejin.im/post/6844903761979113479" >又被node的eventloop坑了，这次是node的锅<i class="fas fa-external-link-alt"></i></a>》。</p>
<p>如果v10版本上述结果存在两种情况：</p>
<ul>
<li>如果time2定时器已经在执行队列中了</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">start</span><br><span class="line">end</span><br><span class="line">promise3</span><br><span class="line">timer1</span><br><span class="line">timer2</span><br><span class="line">promise1</span><br><span class="line">promise2</span><br></pre></td></tr></table></figure>

<ul>
<li>如果time2定时器没有在执行对列中，执行结果为</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">start</span><br><span class="line">end</span><br><span class="line">promise3</span><br><span class="line">timer1</span><br><span class="line">promise1</span><br><span class="line">timer2</span><br><span class="line">promise2</span><br></pre></td></tr></table></figure>

<p>具体情况可以参考<code>poll</code>阶段的两种情况。</p>
<p>从下图可能更好理解：</p>
<p><img src="https://gitee.com/theBoat/picgo/raw/master/%E5%8A%A8%E7%94%BB.gif" alt="动画"></p>
<h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><blockquote>
<p>为什么要有EventLoop？</p>
</blockquote>
<p>因为JS是一门单线程语言，EventLoop是JS环境运行的执行机制，分为浏览器环境跟node环境，能让程序有序执行，避免阻塞。</p>
<p>在代码执行环境中，有执行栈，宏任务队列和微任务队列</p>
<p>宏任务队列存放的是setTimeout，setInterval，I/O等方法</p>
<p>微任务队列存放的是promise，process.nextTick等方法</p>
<p>而宏任务可包含着微任务，当执行完一个宏任务后，会检测该宏任务是否有微任务，有则执行微任务，无则执行下一个宏任务，这就是宏任务与微任务的执行顺序问题</p>
<p>当进入一个执行环境的时候，整个JS脚本相当于一个宏任务，首先会先执行同步代码以及微任务，例如promise的同步任务，然后再执行promise的异步任务也就是then后面的内容</p>
<p>然后会再执行宏任务，setTimeout</p>
<h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p>MDN：执行栈 <a class="link"   target="_blank" rel="noopener" href="https://developer.mozilla.org/zh-CN/docs/Glossary/Call_stack" >https://developer.mozilla.org/zh-CN/docs/Glossary/Call_stack<i class="fas fa-external-link-alt"></i></a></p>
<p>一次弄懂Event Loop（彻底解决此类面试问题）：<a class="link"   target="_blank" rel="noopener" href="https://juejin.cn/post/6844903764202094606" >https://juejin.cn/post/6844903764202094606<i class="fas fa-external-link-alt"></i></a></p>
<p>前端面试之道：<a class="link"   target="_blank" rel="noopener" href="https://juejin.cn/book/6844733763675488269/section/6844733763805511693" >https://juejin.cn/book/6844733763675488269/section/6844733763805511693<i class="fas fa-external-link-alt"></i></a></p>
<p>微任务、宏任务与Event-Loop：<a class="link"   target="_blank" rel="noopener" href="https://juejin.cn/post/6844903657264136200" >https://juejin.cn/post/6844903657264136200<i class="fas fa-external-link-alt"></i></a></p>
<p>带你彻底弄懂Event Loop：<a class="link"   target="_blank" rel="noopener" href="https://segmentfault.com/a/1190000016278115" >https://segmentfault.com/a/1190000016278115<i class="fas fa-external-link-alt"></i></a></p>
<p>这一次，彻底弄懂 JavaScript 执行机制：<a class="link"   target="_blank" rel="noopener" href="https://juejin.cn/post/6844903512845860872" >https://juejin.cn/post/6844903512845860872<i class="fas fa-external-link-alt"></i></a></p>

        </div>

        

        
            <ul class="post-tags-box">
                
                    <li class="tag-item">
                        <a href="/tags/%E8%BF%90%E8%A1%8C%E5%8E%9F%E7%90%86/">#运行原理</a>&nbsp;
                    </li>
                
            </ul>
        

        
            <div class="article-nav">
                
                    <div class="article-prev">
                        <a class="prev"
                           rel="prev"
                           href="/2021/07/21/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8Gitee%E6%90%AD%E5%BB%BA%E5%9B%BE%E5%BA%8A%EF%BC%9F/"
                        >
                            <span class="left arrow-icon flex-center">
                              <i class="fas fa-chevron-left"></i>
                            </span>
                            <span class="title flex-center">
                                <span class="post-nav-title-item">如何使用Gitee免费搭建图床?</span>
                                <span class="post-nav-item">Prev posts</span>
                            </span>
                        </a>
                    </div>
                
                
                    <div class="article-next">
                        <a class="next"
                           rel="next"
                           href="/2021/06/03/HTML-%E8%AF%AD%E4%B9%89%E5%8C%96/"
                        >
                            <span class="title flex-center">
                                <span class="post-nav-title-item">HTML-语义化</span>
                                <span class="post-nav-item">Next posts</span>
                            </span>
                            <span class="right arrow-icon flex-center">
                              <i class="fas fa-chevron-right"></i>
                            </span>
                        </a>
                    </div>
                
            </div>
        

        
    </div>
</div>


                
            </div>

        </div>

        <div class="page-main-content-bottom">
            <footer class="footer">
    <div class="info-container">
        <div class="copyright-info info-item">
            &copy;
            
              <span>2020</span>
              -
            
            2022&nbsp;<i class="fas fa-heart icon-animate"></i>&nbsp;<a href="/">Tckapco</a>
        </div>
        
        <div class="theme-info info-item">
            Powered by <a target="_blank" href="https://hexo.io">Hexo</a>&nbsp;|&nbsp;Theme&nbsp;<a class="theme-version" target="_blank" href="https://github.com/XPoet/hexo-theme-keep">Keep v3.4.5</a>
        </div>
        
        
    </div>
</footer>

        </div>
    </div>

    
        <div class="post-tools">
            <div class="post-tools-container">
    <ul class="tools-list">
        <!-- TOC aside toggle -->
        
            <li class="tools-item page-aside-toggle">
                <i class="fas fa-outdent"></i>
            </li>
        

        <!-- go comment -->
        
    </ul>
</div>

        </div>
    

    <div class="right-bottom-side-tools">
        <div class="side-tools-container">
    <ul class="side-tools-list">
        <li class="tools-item tool-font-adjust-plus flex-center">
            <i class="fas fa-search-plus"></i>
        </li>

        <li class="tools-item tool-font-adjust-minus flex-center">
            <i class="fas fa-search-minus"></i>
        </li>

        <li class="tools-item tool-expand-width flex-center">
            <i class="fas fa-arrows-alt-h"></i>
        </li>

        <li class="tools-item tool-dark-light-toggle flex-center">
            <i class="fas fa-moon"></i>
        </li>

        <!-- rss -->
        

        
            <li class="tools-item tool-scroll-to-top flex-center">
                <i class="fas fa-arrow-up"></i>
            </li>
        

        <li class="tools-item tool-scroll-to-bottom flex-center">
            <i class="fas fa-arrow-down"></i>
        </li>
    </ul>

    <ul class="exposed-tools-list">
        <li class="tools-item tool-toggle-show flex-center">
            <i class="fas fa-cog fa-spin"></i>
        </li>
        
    </ul>
</div>

    </div>

    
        <aside class="page-aside">
            <div class="post-toc-wrap">
    <div class="post-toc">
        <ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#1-%E8%BF%9B%E7%A8%8B%E4%B8%8E%E7%BA%BF%E7%A8%8B"><span class="nav-number">1.</span> <span class="nav-text">1.进程与线程</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%A6%82%E5%BF%B5"><span class="nav-number">1.1.</span> <span class="nav-text">概念</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%A4%9A%E7%BA%BF%E7%A8%8B"><span class="nav-number">1.2.</span> <span class="nav-text">多线程</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#2-%E6%89%A7%E8%A1%8C%E6%A0%88-Call-stack"><span class="nav-number">2.</span> <span class="nav-text">2.执行栈(Call stack)</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#3-%E5%AE%8F%E4%BB%BB%E5%8A%A1%E4%B8%8E%E5%BE%AE%E4%BB%BB%E5%8A%A1"><span class="nav-number">3.</span> <span class="nav-text">3.宏任务与微任务</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%8C%BA%E5%88%AB"><span class="nav-number">3.1.</span> <span class="nav-text">区别</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%AE%8F%E4%BB%BB%E5%8A%A1"><span class="nav-number">3.2.</span> <span class="nav-text">宏任务</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%BE%AE%E4%BB%BB%E5%8A%A1"><span class="nav-number">3.3.</span> <span class="nav-text">微任务</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#4-EventLoop"><span class="nav-number">4.</span> <span class="nav-text">4.EventLoop</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%B5%8F%E8%A7%88%E5%99%A8"><span class="nav-number">4.1.</span> <span class="nav-text">浏览器</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Node"><span class="nav-number">4.2.</span> <span class="nav-text">Node</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E6%80%BB%E7%BB%93"><span class="nav-number">5.</span> <span class="nav-text">总结</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E5%8F%82%E8%80%83"><span class="nav-number">6.</span> <span class="nav-text">参考</span></a></li></ol>
    </div>
</div>
        </aside>
    

    <div class="image-viewer-container">
    <img src="">
</div>


    

</main>



<script src="//cdn.jsdelivr.net/npm/hexo-theme-keep@3.4.5/source/js/utils.js"></script><script src="//cdn.jsdelivr.net/npm/hexo-theme-keep@3.4.5/source/js/main.js"></script><script src="//cdn.jsdelivr.net/npm/hexo-theme-keep@3.4.5/source/js/header-shrink.js"></script><script src="//cdn.jsdelivr.net/npm/hexo-theme-keep@3.4.5/source/js/back2top.js"></script><script src="//cdn.jsdelivr.net/npm/hexo-theme-keep@3.4.5/source/js/dark-light-toggle.js"></script>




    <script src="//cdn.jsdelivr.net/npm/hexo-theme-keep@3.4.5/source/js/code-copy.js"></script>




<div class="post-scripts">
    
        <script src="//cdn.jsdelivr.net/npm/hexo-theme-keep@3.4.5/source/js/left-side-toggle.js"></script><script src="//cdn.jsdelivr.net/npm/hexo-theme-keep@3.4.5/source/js/libs/anime.min.js"></script><script src="//cdn.jsdelivr.net/npm/hexo-theme-keep@3.4.5/source/js/toc.js"></script>
    
</div>



</body>
</html>
